/*******************************************************
* Open Source for Iris : OSIRIS
* Version : 4.0
* Date : 2011
* Author : Guillaume Sutra, Telecom SudParis, France
* License : BSD
********************************************************/

#include <fstream>
#include <iterator>
#include <stdexcept>
#include "OsiManager.h"
#include "OsiStringUtils.h"

using namespace std ;

namespace osiris
{


    // CONSTRUCTORS & DESTRUCTORS
    /////////////////////////////


    // Default constructor
    OsiManager::OsiManager ( )
    {
        initConfiguration() ;   
		loadGaborFilters() ;
		loadApplicationPoints() ;
    }

    // Default destructor
    OsiManager::~OsiManager ( )
    {
        // Release matrix for application points
        if ( mpApplicationPoints )
        {
            cvReleaseMat(&mpApplicationPoints) ;
        }
        
        // Release matrix for Gabor filters
        for ( int f = 0 ; f < mGaborFilters.size() ; f++ )
        {
            cvReleaseMat(&mGaborFilters[f]) ;
        }
    }

    // OPERATORS
    ////////////


    // Initialize all configuration parameters
    void OsiManager::initConfiguration ( )
    {
        // Options of processing
        mProcessSegmentation = false ;
        mProcessNormalization = false ;
        mProcessEncoding = false ;
        mProcessMatching = false ;
        mUseMask = true ;

        // Inputs
        mListOfImages.clear() ;
        mFilenameListOfImages = "" ;
        mInputDirOriginalImages = "./SourceImage/" ;
        mInputDirMasks = "" ;
        mInputDirParameters = "" ;
        mInputDirNormalizedImages = "" ;
        mInputDirNormalizedMasks = "./NormalizedMasks/" ;
        mInputDirIrisCodes = "./IrisCodes/" ;

        // Outputs
        mOutputDirSegmentedImages = "./SegmentedImages/" ;
        mOutputDirParameters = "./CircleParameters/" ;
        mOutputDirMasks = "./Masks/" ;
        mOutputDirNormalizedImages = "./NormalizedImages/" ;
        mOutputDirNormalizedMasks = "./NormalizedMasks/" ;
        mOutputDirIrisCodes = "./IrisCodes/" ;
        mOutputFileMatchingScores = "" ;

        // Parameters
        mMinPupilDiameter = 50 ;
        mMaxPupilDiameter = 160 ;
        mMinIrisDiameter = 160 ;
        mMaxIrisDiameter = 280 ;
        mWidthOfNormalizedIris = 512 ;
        mHeightOfNormalizedIris = 64 ;
        mFilenameGaborFilters = "./filters.txt" ;
        mFilenameApplicationPoints = "./points.txt" ;
        mGaborFilters.clear() ;
        mpApplicationPoints = 0 ;

        // Suffix for filenames
        mSuffixSegmentedImages = "_segm.bmp" ;
        mSuffixParameters = "_para.txt" ;
        mSuffixMasks = "_mask.bmp" ;
        mSuffixNormalizedImages = "_imno.bmp" ;
        mSuffixNormalizedMasks = "_mano.bmp" ;
        mSuffixIrisCodes = "_code.bmp" ;
    }


    // Load the Gabor filters (matrix coefficients) from a textfile
    void OsiManager::loadGaborFilters ( )
    {
        // Open text file containing the filters
        ifstream file(mFilenameGaborFilters.c_str(),ios::in) ;
        if ( ! file )
        {
            throw runtime_error("Cannot load Gabor filters in file " + mFilenameGaborFilters) ;
        }
        
        // Get the number of filters
        int n_filters ;
        file >> n_filters ;
        mGaborFilters.resize(n_filters) ;

        // Size of filter
        int rows , cols ;

        // Loop on each filter
        for ( int f = 0 ; f < n_filters ; f++ )
        {    
            // Get the size of the filter
            file >> rows ;
            file >> cols ;

            // Temporary filter. Will be destroyed at the end of loop
            mGaborFilters[f] = cvCreateMat(rows,cols,CV_32FC1) ;            
            
            // Set the value at coordinates r,c
            for ( int r = 0 ; r < rows ; r++ )
            {
                for ( int c = 0 ; c < cols ; c++ )
                {
                    file >> mGaborFilters[f]->data.fl[r*cols+c] ;
                }
            }

        } // Loop on each filter
        
        // Close the file
        file.close() ;

    } // end of function





    // Load the application points (build a binary matrix) from a textfile
    void OsiManager::loadApplicationPoints ( )
    {
        // Open text file containing the filters
        ifstream file(mFilenameApplicationPoints.c_str(),ios::in) ;
        if ( ! file )
        {
            throw runtime_error("Cannot load the application points in " + mFilenameApplicationPoints) ;
        }
        
        // Get the number of points
        int n_points = 0 ;
        file >> n_points ;

        // Allocate memory for the matrix containing the points
        mpApplicationPoints = cvCreateMat(mHeightOfNormalizedIris,mWidthOfNormalizedIris,CV_8UC1) ;

        // Initialize all pixels to "off"
        cvSet(mpApplicationPoints,cvScalar(0)) ;        

        // Local variables
        int i , j ;

        // Loop on each point
        for ( int p = 0 ; p < n_points ; p++ )
        {    
            // Get the coordinates
            file >> i ; file >> j ;
            
            // Set pixel to "on"
            if ( i < 0 || i > mpApplicationPoints->rows-1 || j < 0 || j > mpApplicationPoints->cols-1 )
            {
                cout << "Point (" << i << "," << j << ") " ;
                cout << "exceeds size of normalized image : " ;
                cout << mpApplicationPoints->rows << "x" << mpApplicationPoints->cols ;
                cout << " while loading application points" << endl ;
            }
            else
            {
                mpApplicationPoints->data.ptr[(i)*mpApplicationPoints->cols+j] = 255 ;
            }
        }

        // Close the file
        file.close() ;

    } // end of function




    // Load, segment, normalize, encode, and save according to user configuration
   int OsiManager::processEye ( const string & rFileName , OsiEye & rEye )
	{
		// Strings handle
		OsiStringUtils osu ;
		// Get eye name
		string short_name = osu.extractFileName(rFileName);
		rEye.loadOriginalImage(mInputDirOriginalImages+rFileName); 
	
		/////////////////////////////////////////////////////////////////
		// SEGMENTATION : process, load
		 rEye.segment(mMinIrisDiameter,mMinPupilDiameter,mMaxIrisDiameter,mMaxPupilDiameter) ;
		// Save segmented image
		rEye.saveSegmentedImage(mOutputDirSegmentedImages+short_name+mSuffixSegmentedImages) ;
 
		/////////////////////////////////////////////////////////////////
		// NORMALIZATION : process, load
		rEye.normalize(mWidthOfNormalizedIris,mHeightOfNormalizedIris) ;
 
		/////////////////////////////////////////////////////////////////
		// ENCODING : process, load
		rEye.encode(mGaborFilters) ;
 
		/////////////////////////////////////////////////////////////////
		// SAVE
		// Save parameters
		rEye.saveParameters(mOutputDirParameters+short_name+mSuffixParameters) ;
		// Save mask
		rEye.saveMask(mOutputDirMasks+short_name+mSuffixMasks) ;
		// Save normalized image
		rEye.saveNormalizedImage(mOutputDirNormalizedImages+short_name+mSuffixNormalizedImages) ;
		// Save normalized mask
		rEye.saveNormalizedMask(mOutputDirNormalizedMasks+short_name+mSuffixNormalizedMasks) ;
		// Save iris code
		rEye.saveIrisCode(mOutputDirIrisCodes+short_name+mSuffixIrisCodes) ;  
 
		return 0;
	} // end of function
 
	int OsiManager::loadEye ( const string & rFileName , OsiEye & rEye )
	{
		OsiStringUtils osu ;
		string short_name = osu.extractFileName(rFileName) ;
 
		// Load normalized mask
		rEye.loadNormalizedMask(mInputDirNormalizedMasks+short_name+mSuffixNormalizedMasks) ;
 
		// Load iris code
		rEye.loadIrisCode(mInputDirIrisCodes+short_name+mSuffixIrisCodes) ;
 
		return 0;
	} // end of function
 
	void OsiManager::process(string filename)
	{
		try
		{
			OsiEye eye ;
			processEye(filename, eye) ;  
		}
		catch ( exception & e )
		{
			cout << e.what() << endl ;                
		}
	}
 
	float OsiManager::match(string filename1, string filename2)
	{
		float val = 0;
 
		try
		{
			OsiEye eye1, eye2 ;
			loadEye(filename1, eye1) ;  
			loadEye(filename2, eye2) ; 
			val = eye1.match(eye2, mpApplicationPoints);
		}
		catch ( exception & e )
		{
			cout << e.what() << endl ;                
		}
 
		return val;
	}
 
} // end of namespace


